var pow_wsserv = null;

/**
	* 
	* Server that can be instantiated. How long until httpd.js catches up? 
	* 
	*/
function pow_wsServ(server) {
	this.query_pairs = new Array();
	this.pairs = new Array();
	this.whole_get = "";
	this.root_directory = pow_bc_get_root_directory();
	this.index_directory = pow_bc_get_index_directory();
	this.content_type = "text/html";
	this.response_code = "200 OK";
	// PUBLIC
	this.REQUEST = new Array();
	this.GET =  new Array();
	this.POST =  new Array();
	this.REWRITE_RULES =  new Array();
	this.REQUEST_HEADERS =  new Array();
	this.RAW_POST = "";
	this.RAW_REQUEST = "";
	this.REQUEST_METHOD = "GETWS";
	// END PUBLIC
	this.server = server;
	this.wsserv = null;
	this.crontab_on = false;

	this.async_listener = new wsAsync(this);

	this.start_server = function(port) {
		try {
			var loopback_only = pow_bc_get_loopback_only();
			var sock = Components.classes["@mozilla.org/network/server-socket;1"];
			pow_wsserv = sock.createInstance(Components.interfaces.nsIServerSocket);
/*
 var serverSocket=Components.classes["@mozilla.org/network/server-socket;1"] 
 .createInstance(Components.interfaces.nsIServerSocket); 
 serverSocket.init(80,true,5); 
*/
			//pow_wsserv = pow_wsserv.QueryInterface(Components.interfaces.nsIServerSocket);
			pow_wsserv.init(port, loopback_only, -1);
			pow_wsserv.asyncListen(this.async_listener);
			this.wsserv = pow_wsserv;
			//window.addEventListener('unload', this.stop_wsserver, false);
			return true;
		} catch (e) {
			log_error(e);
			return false;
		}
	}

	/**
    * 
    * Called upon window close or any stop of the server.
    * 
	  */

	this.stop_wsserver = function() {
		// UPON killing a window
		try {
			//pow_server.serv.close();
			//pow_server_active = false;
		} catch (e) {
			//log_error(e); return "";
		}
	}

	/*
   *
   * Logs access to the server with timestamp. Logged in "/pow/logs/access.txt"
   *
	 */

  // PUBLIC
	this.prime_pump = function(listener, stream) {
 	  this.pow_pump = Components.
 	  	classes["@mozilla.org/network/input-stream-pump;1"].
 	  createInstance(Components.interfaces.nsIInputStreamPump);
 		//this.pow_pump.init(stream, -1, -1, 1000000, 100, true); // 8 sec/100 google.gif
		this.pow_pump.init(stream, -1, -1, 1000000, 100, false);
 	 	this.pow_pump.asyncRead(listener, null);
	};

	this.log_wsaccess = function(access_msg) {
		var date_string = pow_get_pretty_date();
		access_msg = date_string+access_msg;
		pow_log(access_msg,"/pow/log/wsaccess.txt","/pow/log/wsaccess.1.txt");
	}
} // END pow_Server

function wsAsync(wsserver) {

	 /*
    *
    * Called when the server is stopped in any way, such as a window closing.
    *
	  */

	this.onStopListening = function(serv, status) {
		// noop
	};

	 /*
    *
		* Called after connection established
    *
	  */
	this.onSocketAccepted = function(serv, transport) {
		try {
			// CLEAR OLD DATA
			pow_request = new Pow_Request();
			pow_response = new Pow_Response();
			// END CLEAR

			// INIT STREAMS
			var stream = transport.openInputStream(0,0,0);
			var instream = Components.classes["@mozilla.org/scriptableinputstream;1"]
				.createInstance(Components.interfaces.nsIScriptableInputStream);
			instream.init(stream);
			var outstream = transport.openOutputStream(0,10000000,100000);
			// END STREAMS

			// CONFIGURE TRANSPORT
			//if(transport.setTimeout) { // FF 1.0.0 does not allow this
			//	transport.setTimeout(1, 5); // 5s timeout
			//}
			// END CONFIGURE TRANSPORT

	 		// ソケットからデータが来たらwsDataListさんに渡す
	 		this.dataListener = new wsDataList( wsserver, instream, outstream, stream );
			// PLACE DOS byte-by-byte data stop here
			wsserver.prime_pump(this.dataListener, stream);
			// END READ DATA
		} catch (e) {
			log_error(e); return "";
		} // END TRY
	} // END Socket accepted
} // END wsAsync

 /*
  *
  * Data Listener object that shows what to do with data once received.
  *
  */

function wsDataList( wsserver, instream, outstream, stream ) {
	this.found_return = 0;
	this.data_in = "";

  /*
   *
   * Called when listening startedd, before data is received. Noop.
   *
   */

	this.onStartRequest = function(request, context) { };

  /*
   *
   * Called when server stopped. Ties loose ends.
   *
   */

	this.onStopRequest = function(request, context, status) {
     instream.close();
     outstream.close();
	};

   /*
  *
  * Called when data comes in.
  *
  */

	this.onDataAvailable = function( request, 
																context, 
																inputStream, 
																offset, 
																count ) {

		// TODO: リクエストを見て生成するよう変更する必要あり!
		var strResponse = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" +
			"Upgrade: WebSocket" + "\r\n" +
			"Connection: Upgrade" + "\r\n" +
			"WebSocket-Origin: _origin_" + "\r\n" +
			"WebSocket-Location: ws://127.0.0.1:10081/\r\n" + "\r\n";

		try {
			// SET UP VARS
			var query_pairs = new Array();
			var extra_headers = "";
			var content_location = "";
			var request_valid = true;
			var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
     			.createInstance( Components.interfaces.nsIBinaryInputStream );
			bis.setInputStream(stream);
			
			// data_chunkとwschunk(echoすべき受信データの配列)を作成
			var wschunks = new Array();
			var wschunk_tmp = "";
			var data_chunk = "";
			var flgData = false;
			for(var i=0;i<count;i++){
				var tmpChar = bis.read8();
				if(tmpChar == 0xff){
					// websocketsのお尻はこれでみつけられる
					// wsserver.log_wsaccess("found: " + i);
					if(flgData){
						flgData = false;

						// ロジック側から取り出すためにWebSocketsでもらった値を格納
						pow_server.RAW_POST = wschunk_tmp;
						
						// WebSocketsの処理ロジック(今はまだ固定ファイル名)
						var tmpContStr = pow_get_file(pow_bc_get_index_directory()+"/htdocs/WebSockets.js","rb");						
						tmpContStr = process_server_js(tmpContStr);

						// Content-Locationヘッダを削除
						var tmpContAry = tmpContStr.split(/\n/);
						tmpContAry = tmpContAry.slice(2);
						wschunk_tmp = tmpContAry.join("\n");

						// sjs処理器はserver.jsにあるもののコピーを持っている(プライベートで呼び出せない・・・)
						// wschunk_tmp = process_server_js(tmpContStr);						

						// ヘッダとフッター(テイラー?)をつけるのはこれが正解
						wschunks.push("\u0000"+wschunk_tmp+"\uffff");						
						wschunk_tmp = "";
					}
					data_chunk = data_chunk + "\uffff";
				}
				if(tmpChar != 0x00 && tmpChar != 0xff){
					// websocketsのスタートは0x00でnullなので
					data_chunk = data_chunk + String.fromCharCode(tmpChar);
					if(flgData){
						wschunk_tmp = wschunk_tmp + String.fromCharCode(tmpChar);
					}
				}
				if(tmpChar == 0x00){
					data_chunk = data_chunk + "\u0000";
					flgData = true;
				}
			}
			
			if(wschunk_tmp.length != 0){
				wsserver.log_wsaccess("chunk data is not processed..");
			}
			
			//var data_chunk = bis.readBytes(count);
			this.data_in += data_chunk;

			// wsserver.log_wsaccess("chunk: >" + data_chunk + "<");
			// wsserver.log_wsaccess("data_in: >" + this.data_in + "<");

			// handshakeの処理
			if(this.data_in.match(/GET .*/)) {
				var get_match = new Array();
				var request_lines = this.data_in.split(/\n/);
				for(var i=0; i<request_lines.length; i++){
					get_match = request_lines[i].match(/^Origin: (.*)/m);
					if(get_match){
						strResponse = strResponse.replace("_origin_",get_match[1]);
						wsserver.log_wsaccess("Orign:" + get_match[1]);
						myDump(get_match[1]);
					}
					//wsserver.log_wsaccess("ReqHeader:" + request_lines[i]);
				}
				if(strResponse.match(/_origin_/)){
					strResponse = "HTTP/1.1 500 No socket origin found.\r\n";
					wsserver.log_wsaccess("WebSockets origin was incorrect!");
				}
				outstream.write(strResponse, strResponse.length);
				outstream.flush();
				this.data_in = "";
			} else {
				if(wschunks[0] != null){

					for(var i = 0; i < wschunks.length; i++) {
						strResponse = wschunks[i];
						outstream.write(strResponse, strResponse.length);
						outstream.flush();
					}

					//this.data_in = wschunks[wschunks.length - 1];
					this.data_in = "";
					wschunks = new Array();
					
				} else {
					wsserver.log_wsaccess(wsserver.REQUEST_METHOD+
						" "+this.data_in+" cont."+strResponse);
				}
			}
			wsserver.log_wsaccess(wsserver.REQUEST_METHOD+
				" "+this.data_in+" "+strResponse);
		} catch (e) {
			log_error(e); return "";
		} // END TRY
	} // END ON DATA AVAILABLE

} // END DataList

// sjs処理器(server.jsよりコピー)
function process_server_js(webpage) {
	try {
		var raw_webpage = webpage;
		webpage = (function(webpage) {
		var document = new pow_Document();
		var m = webpage.match(/Content-Location: (.*)/m);
		var content_location = m[1];
		// escape "?"
		webpage = webpage.replace(/\?/g,"__QUEST__");
		webpage = webpage.replace(/<__QUEST__sjs/g,"<?sjs");
		webpage = webpage.replace(/__QUEST__>/g,"?>");

		var lost_lines = 0;
		if(webpage.indexOf("?") == -1 ) {
			m = false;
		} else {
			m = webpage.match(/([^\?]*)<\?sjs(([^\?]|\n)*)\?>/m);
		}
		var function_offset = 0;
		try {
			throw_error(); // dummy function to find line number
		} catch(e) {
			function_offset = e.lineNumber - 200;
		}

		while(m && pow_response.exit_flag == false) {
			var num_rets = m[1].split(/\n/);
			var offset = num_rets.length + lost_lines + 36;
			var script = m[2];
			script = script.replace(/__QUEST__/g,"?");
			try {
				// pow_response.eval did not scope as intended ...
				// Since document could not print anything in response's scope
				// pow_response.eval(script);
				eval(script);
			} catch (e if e == "exit_flag") {
				pow_response.exit_flag = true;
			} catch (e if e == "highlight_flag") {
				pow_response.exit_flag = true;
				pow_response.highlight_flag = true;
			} catch (e if e == "debug_flag") {
				pow_response.exit_flag = true;
				pow_response.debug_flag = true;
			} catch (e) {
				e.lineNumber -=  function_offset - offset;
				document.write('Error: '+pow_request.req_file+':'+
						e.lineNumber+' '+e.message);
			}

			var before_length = webpage.split(/\n/).length;
			var webpage = webpage.replace( /<\?sjs(([^\?]|\n)*)\?>/m, 
					pow_response.script_out );
			webpage = webpage.replace(/__DOLLAR__/mg,"$");
			lost_lines += before_length - webpage.split(/\n/).length;
			pow_response.script_out = "";
			if(webpage.indexOf("<?sjs") == -1 ) {
				m = false;
			} else {
				m = webpage.match(/([^\?]*)<\?sjs(([^\?]|\n)*)\?>/m);
			}
		}
		webpage = webpage.replace(/__QUEST__/g,"?");
		webpage = webpage.replace(/Content-Length: \d+\r\n/m,"");
		return webpage;
		})(webpage);
		if(pow_response.highlight_flag) {
			webpage = pow_highlight_code(raw_webpage);
		} else if(pow_response.debug_flag) {
			webpage = pow_create_client_code(raw_webpage);
		} else if(pow_response.exit_flag) {
			webpage = webpage.replace(/__EXIT_FLAG__(.|\n|\r)*/m,"");
		}
		if(pow_response.clear_flag) {
			webpage = webpage.replace(/\r\n\r\n(.|\n|\r)*__CLEAR_FLAG__/m,"\r\n\r\n");
		}
		// finally check SESSION and save
		for(var i in pow_server.SESSION) {
			pow_session_end();
			break;
		}
		return webpage;
	} catch(e) {
		log_error(e); return "";
	}
}

// Firefoxのjavascriptエラーコンソールにメッセージを送る
function myDump(aMessage) {
	var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
		.getService(Components.interfaces.nsIConsoleService);
	consoleService.logStringMessage("My component: " + aMessage);
}